package com.zp.manager.auth;

import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.json.JSONUtil;
import com.zp.constants.SpaceUserPermissionConstant;
import com.zp.constants.UserConstant;
import com.zp.domain.entity.Picture;
import com.zp.domain.entity.Space;
import com.zp.domain.entity.SpaceUser;
import com.zp.domain.entity.User;
import com.zp.enums.ErrorCode;
import com.zp.enums.SpaceTypeEnum;
import com.zp.enums.SpaceUserRoleEnum;
import com.zp.exception.BusinessException;
import com.zp.service.PictureService;
import com.zp.service.SpaceService;
import com.zp.service.SpaceUserService;
import com.zp.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 权限认证(自定义权限加载接口实现类)
 */
@Component
@RequiredArgsConstructor
public class StpInterfaceImpl implements StpInterface {
    private final SpaceUserAuthManager spaceUserAuthManager;
    private final SpaceUserService spaceUserService;
    private final UserService userService;
    private final PictureService pictureService;
    private final SpaceService spaceService;
    /**
     * 获取 "/api" 固定前缀，提供默认值以避免占位符解析失败
     */
    @Value("${server.servlet.context-path:/api}")
    private String contentPath;

    /**
     * 返回权限码集合
     *
     * @param loginId   账号id
     * @param loginType 账号类型
     * @return 权限码集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 1. 判断要校验的账号类型
        if (!StpKit.SPACE_TYPE.equals(loginType)) {
            // 不是空间账号, 不需要进行校验
            return Collections.emptyList();
        }
        // 2. 先获取管理员权限, 后续会频繁使用
        List<String> ADMIN_PERMISSIONS = spaceUserAuthManager.getPermissionsByRole(SpaceUserRoleEnum.ADMIN.getValue());
        // 3. 获取上下文对象, 动态判断
        com.zp.manager.auth.SpaceUserAuthContext authContext = this.getSpaceUserAuthContext();
        // 4. 上下文字段都为空, 表示公共图库, 直接返回管理员权限
        if (this.isAllFieldsNull(authContext)) {
            return ADMIN_PERMISSIONS;
        }
        // 获取 UserId
        // 为避免用户角色变更后权限不生效，这里从数据库获取最新用户信息
        Long userId = Long.parseLong(String.valueOf(loginId));
        User loginUser = userService.getById(userId);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        }
        // 同步最新用户信息到 Sa-Token 会话，保持状态一致
        try {
            StpKit.SPACE.getSessionByLoginId(loginId).set(UserConstant.USER_LOGIN_STATE, loginUser);
        } catch (Exception ignored) {}
        // 5. 优先从上下文获取 SpaceUser
        SpaceUser spaceUser = authContext.getSpaceUser();
        if (spaceUser != null) {
            return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
        }
        // 6. 其次获取 SpaceUserId, 查数据库
        Long spaceUserId = authContext.getSpaceUserId();
        if (spaceUserId != null) {
            spaceUser = spaceUserService.getById(spaceUserId);
            if (spaceUser == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "空间用户不存在");
            }
            // 获取当前登录用户对应的 SpaceUser
            SpaceUser loginSpaceUser = spaceUserService.lambdaQuery()
                    .eq(SpaceUser::getSpaceId, spaceUser.getSpaceId())
                    .eq(SpaceUser::getUserId, userId)
                    .one();
            if (loginSpaceUser == null) {
                return Collections.emptyList();
            }
            // 防止管理员在私有空间没有权限
            return spaceUserAuthManager.getPermissionsByRole(loginSpaceUser.getSpaceRole());
        }
        // 7. 如果没有 spaceUserId, 尝试通过 spaceId 或 pictureId 获取 Space 对象并处理
        Long spaceId = authContext.getSpaceId();
        if (spaceId == null) {
            // 如果没有 spaceId, 通过 pictureId 获取 Picture 对象和 Space 对象
            Long pictureId = authContext.getPictureId();
            // 图片 id 也没有, 则默认通过权限校验
            if (pictureId == null) {
                return ADMIN_PERMISSIONS;
            }
            Picture picture = pictureService.lambdaQuery()
                    .eq(Picture::getId, pictureId)
                    .select(Picture::getId, Picture::getSpaceId, Picture::getUserId)
                    .one();
            if (picture == null) {
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "未找到图片信息");
            }
            spaceId = picture.getSpaceId();
            // 公共图库, 仅本人或管理员可操作
            if (spaceId == null) {
                if (picture.getUserId().equals(userId) || userService.isAdmin(loginUser)) {
                    return ADMIN_PERMISSIONS;
                } else {
                    // 不是自己的图片, 仅可查看
                    return Collections.singletonList(com.zp.constants.SpaceUserPermissionConstant.PICTURE_VIEW);
                }
            }
        }
        // 获取 Space 对象
        Space space = spaceService.getById(spaceId);
        if (space == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "未找到空间信息");
        }
        // 根据 Space 类型判断权限
        if (ObjUtil.equals(space.getSpaceType(), SpaceTypeEnum.PRIVATE.getValue())) {
            // 私有空间, 仅本人或管理员有权限
            if (space.getUserId().equals(userId) || userService.isAdmin(loginUser)) {
                return ADMIN_PERMISSIONS;
            } else {
                return Collections.emptyList();
            }
        } else {
            // 团队空间, 查询 SpaceUser 并获取角色和权限
            spaceUser = spaceUserService.lambdaQuery()
                    .eq(SpaceUser::getSpaceId, spaceId)
                    .eq(SpaceUser::getUserId, userId)
                    .one();
            if (spaceUser == null) {
                return Collections.emptyList();
            }
            return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
        }
    }

    /**
     * 返回角色集合(本项目用不到)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        return Collections.emptyList();
    }


    /**
     * 从请求中获取上下文对象
     *
     * @return 上下文对象
     */
    public com.zp.manager.auth.SpaceUserAuthContext getSpaceUserAuthContext() {
        // 1. 获取到 HttpServletRequest
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        // 2. 拿到 Content-Type
        String contentType = request.getHeader(Header.CONTENT_TYPE.getValue());
        com.zp.manager.auth.SpaceUserAuthContext spaceUserAuthContext;
        // 3.获取请求参数
        if (ContentType.JSON.getValue().equals(contentType)) {
            // POST 请求
            String body = ServletUtil.getBody(request);
            spaceUserAuthContext = JSONUtil.toBean(body, com.zp.manager.auth.SpaceUserAuthContext.class);
        } else {
            // GET 请求
            Map<String, String> paramMap = ServletUtil.getParamMap(request);
            spaceUserAuthContext = BeanUtil.toBean(paramMap, com.zp.manager.auth.SpaceUserAuthContext.class); // 对象转对象
        }
        // 4. 根据请求路径区分 id 的含义
        Long id = spaceUserAuthContext.getId();
        if (ObjUtil.isNotNull(id)) {
            // 获取请求路径, localhost:8080/api/space/getSpaceById
            String requestURI = request.getRequestURI();
            // 去掉 "/api/", 此时剩下 "space/getSpaceById"
            String uri = requestURI.replaceAll(contentPath + "/", "");
            // 获取业务前缀, space、picture等(截取字符串 uri "/" 之前的内容, 不是最后一个 "/")
            String bizName = StrUtil.subBefore(uri, "/", false);
            switch (bizName) {
                case "space":
                    spaceUserAuthContext.setSpaceId(id);
                    break;
                case "picture":
                    spaceUserAuthContext.setPictureId(id);
                    break;
                case "spaceUser":
                    spaceUserAuthContext.setSpaceUserId(id);
                    break;
                default:
            }
        }
        return spaceUserAuthContext;
    }


    /**
     * 判断对象所有字段是否为 null
     *
     * @param object 对象
     * @return true 表示所有字段都为 null
     */
    private boolean isAllFieldsNull(Object object) {
        if (object == null) {
            return true; // 对象为 null
        }
        Field[] fields = ReflectUtil.getFields(object.getClass());
        return Arrays.stream(fields)
                // 获取字段值
                .map(field -> ReflectUtil.getFieldValue(object, field))
                // 判断字段值是否全部为空
                .allMatch(ObjUtil::isEmpty);
    }
}
